package org.budo.dubbo.protocol.http.dispatcher;

import java.io.ByteArrayInputStream;
import java.util.Map;

import org.budo.dubbo.protocol.http.BudoDubboHttpProtocol;
import org.budo.dubbo.protocol.http.invocation.BudoDubboRpcInvocation;
import org.budo.dubbo.protocol.http.invocation.adapter.InvocationAdapter;
import org.budo.dubbo.protocol.http.invocation.adapter.ProtocolRequest;
import org.budo.dubbo.protocol.http.invocation.adapter.ProtocolResponse;
import org.budo.dubbo.protocol.http.invocation.adapter.impl.ProtocolRequestInvocationAdapter;
import org.budo.support.lang.util.MapUtil;

import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcResult;
import com.alibaba.fastjson.JSON;

import lombok.extern.slf4j.Slf4j;

/**
 * websocket 在用
 * 
 * @author li
 */
@Slf4j
public class BudoHttpDispatcherUtil {
    public static BudoDubboRpcInvocation adaptInvocation(InvocationAdapter invocationAdapter, ProtocolRequest protocolRequest) {
        try {
            return invocationAdapter.adaptInvocation(protocolRequest);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static Exporter<?> findInterfaceExporter(BudoDubboRpcInvocation invocation, BudoDubboHttpProtocol protocol) {
        String interfaceName = invocation.getInterfaceName();

        if (null == protocol || null == interfaceName) {
            return null;
        }

        Map<String, Exporter<?>> exporterMap = protocol.getExporterMap();
        if (null == exporterMap) {
            return null;
        }

        return exporterMap.get(interfaceName);
    }

    public static InvocationAdapter getInvocationAdapter() {
        return new ProtocolRequestInvocationAdapter();
    }

    public static void renderInvokeResult(ProtocolRequest protocolRequest, ProtocolResponse protocolResponse, Result result) {
        Map<String, Object> responseMap = MapUtil.stringObjectMap("requestId", protocolRequest.getRequestId(), //
                "result", MapUtil.stringObjectMap("value", result.getValue(), "exception", result.getException()));

        String responseJson = JSON.toJSONString(responseMap);
        protocolResponse.write(new ByteArrayInputStream(responseJson.getBytes()));
    }

    public static void onMessage(ProtocolRequest protocolRequest, ProtocolResponse protocolResponse) {
        InvocationAdapter invocationAdapter = BudoHttpDispatcherUtil.getInvocationAdapter();
        BudoDubboRpcInvocation invocation;

        try {
            invocation = BudoHttpDispatcherUtil.adaptInvocation(invocationAdapter, protocolRequest);
        } catch (Throwable e) {
            log.error("#37 adaptInvocation error, e=" + e, e); // TODO 应该走 ViewRender

            BudoHttpDispatcherUtil.renderInvokeResult(protocolRequest, protocolResponse, new RpcResult(e));
            return;
        }

        BudoDubboHttpProtocol protocol = BudoDubboHttpProtocol.getInstance();
        Exporter<?> exporter = BudoHttpDispatcherUtil.findInterfaceExporter(invocation, protocol);

        if (null == exporter) {
            log.error("#41 protocolRequest=" + protocolRequest + ", invocation=" + invocation + ", exporter=" + exporter);
            return;
        }

        Invoker<?> invoker = exporter.getInvoker();
        Result result = invoker.invoke(invocation);

        BudoHttpDispatcherUtil.renderInvokeResult(protocolRequest, protocolResponse, result);
    }
}